package jfprincesstests.finiteautomata;

import java.util.Collections;
import java.util.List;

import jfprincesstests.fakes.automata.FakeNode;
import jfuzzyprincepless.zarchive.FSM.INode;
import jfuzzyprincepless.zarchive.FSM.IOutcome;
import jfuzzyprincepless.zarchive.FSM.Node;
import jfuzzyprincepless.zarchive.FSM.Outcome;
import jfuzzyprincepless.negotiation.DefaultNegotiationContext;
import jfuzzyprincepless.negotiation.INegotiationContext;

import laboratoryQ.computation.BaseUtils;
import laboratoryQ.junit.ignore.patch.BaseTestCase;

public class TestFinitAuthomata extends BaseTestCase { // TODO: rename: Automata
	public void testFiniteAutomataNode() {
		INode node = new Node("Initial");
		assertEquals("Initial", node.getName());
		assertIsInstance(node.getOutcomes(), List.class);
		assertEquals(Collections.EMPTY_LIST, node.getOutcomes());

		INode n0 = new Node("n0");
		INode n1 = new Node("n1");
		BaseUtils.addAll(
			node.getOutcomes(),
			new Outcome(n0),
			new Outcome(n1)
		);
		assertIsInstance(node.getOutcomes().get(0), IOutcome.class);

		n0.getOutcomes().add(new Outcome(node));
		INode n11 = new Node("n11");
		n1.getOutcomes().add(new Outcome(n11));
		n11.getOutcomes().add(new Outcome(node));

		INode curNode = node;
		assertEquals("Initial", curNode.getName());
		curNode = walkNodes(curNode, "n0;Initial;n1;n11");
		assertEquals("n11", curNode.getName());
		curNode = walkNodes(curNode, "Initial;n11");
		assertNull(curNode);
	}

	private INode walkNodes(INode curNode, String path) {
		for (String name : path.split(";")) {
			IOutcome curOut = null;
			for (IOutcome out : curNode.getOutcomes()) {
				if (out.getDestination().getName().equals(name)) {
					curOut = out;
					break;
				}
			}
			if (curOut == null) {
				return null;
			}
			curNode = curOut.getDestination();
		}
		return curNode;
	}

	/**
	 * standard work-flow is: to call method depend on current state,
	 * process outcome algorithm is not depend on environment state.
	 */
	public void testTrivialWorkFlow() {
		StringBuilder sb = new StringBuilder();
		FakeNode fn0 = new FakeNode("0", sb);
		INode n0 = fn0;
		FakeNode fn1 = new FakeNode("1", sb);
		INode n1 = fn1;
		FakeNode fn2 = new FakeNode("2", sb);
		INode n2 = fn2;

		fn0.setNext(n1);
		fn1.setNext(n2);
		fn2.setNext(n0);

		INode cur = n0;
		INegotiationContext context = new DefaultNegotiationContext();
		for (int i = 0; i < 10; i++) {
			IOutcome out = cur.chooseOutcome(context);
			out.onWalk(cur, context);
			cur = out.getDestination();
		}

		assertEquals(
			";choose[0];walk: 0->1;choose[1];walk: 1->2;choose[2];walk: 2->0" +
			";choose[0];walk: 0->1;choose[1];walk: 1->2;choose[2];walk: 2->0" +
			";choose[0];walk: 0->1;choose[1];walk: 1->2;choose[2];walk: 2->0" +
			";choose[0];walk: 0->1",
			sb.toString()
		);

		// ### ### ### ### ### ### ### ### ### ###
		sb.setLength(0);

		fn0.setNext(fn1, fn2);
		cur = n0;
		for (int i = 0; i < 10; i++) {
			IOutcome out = cur.chooseOutcome(context);
			out.onWalk(cur, context);
			cur = out.getDestination();
		}
		assertEquals(
				";choose[0];walk: 0->1;choose[1];walk: 1->2;choose[2];walk: 2->0" +
				";choose[0];walk: 0->2;choose[2];walk: 2->0;choose[0];walk: 0->1" +
				";choose[1];walk: 1->2;choose[2];walk: 2->0;choose[0];walk: 0->2" +
				";choose[2];walk: 2->0",
				sb.toString());
	}
}
